第26天的實作為第22天的延伸,在第22天是當滑鼠移到特定元素會給該元素有highlight的效果,而第26天就做出跟stripe,nav
元素一樣的效果。
如何要做出這特效,原理為取得目標顯示元素的寬、高度和在網頁中的相對位置,並把的寬、高度和位置加到div
元素中,讓div
元素移動到目標目標的位置,並加入CSS的特效。
首先nav
包含要有滑鼠指定元素li
,在li
元素裡有類別為dropdown
的元素。
<nav class="top">
<ul class="cool">
<li>
<a href="#">About Me</a>
<div class="dropdown dropdown1">
。
</div>
</li>
<li>
<a href="#">Courses</a>
<ul class="dropdown courses">
。
</ul>
</li>
<li>
<a href="#">Other Links</a>
<ul class="dropdown dropdown3">
。
</ul>
</li>
</ul>
</nav>
在類別dropdown
中的每一個元素為不顯示display: none
。
.dropdown {
opacity: 0;
position: absolute;
overflow: hidden;
padding:20px;
top:-20px;
border-radius:2px;
transition: all 0.5s;
transform: translateY(100px);
will-change: opacity;
display: none;
}
接下來在nav
元素中的每一個li
元素建立兩個事件,第一個為滑鼠移出事件以及第二個是滑鼠移入事件。
triggers.forEach(trigger => trigger.addEventListener('mouseenter', handleEnter))
triggers.forEach(trigger => trigger.addEventListener('mouseleave', handelLeave))
滑鼠移出事件,為目標元素移除CSS類別即可。
this.classList.remove('trigger-enter', 'trigger-enter-active')
bg.classList.remove('open')
而滑鼠移入事件為,當滑鼠指到li
元素時,加入CSS類別trigger-enter
,trigger-enter
是讓dropdown
的display
轉為block
,加入時間150毫秒之後判斷若有trigger-enter
類別,再加入trigger-enter-active
類別,而trigger-enter-active
類別是將不透明度opacity
從0到1。
function handleEnter(){
this.classList.add('trigger-enter')
setTimeout(()=>{
if (this.classList.contains('trigger-enter')) {
this.classList.add('trigger-enter-active')
}
}, 150)
bg.classList.add('open')
const dropdown = this.querySelector('.dropdown')
const dropdownCoords = dropdown.getBoundingClientRect()
const navCoords = nav.getBoundingClientRect()
const coords = {
height: dropdownCoords.height,
width: dropdownCoords.width,
top: dropdownCoords.top - navCoords.top,
left: dropdownCoords.left - navCoords.left,
}
bg.style.setProperty('height', `${coords.height}px`)
bg.style.setProperty('width', `${coords.width}px`)
bg.style.setProperty('transform', `translate(${coords.left}px, ${coords.top}px)`)
}
將類別dropdownBackground
的元素加入類別open
。
<div class="dropdownBackground">
<span class="arrow"></span>
</div>
效果是將類別open
的div
元素不透明度設為1。
.dropdownBackground.open {
opacity: 1;
}
這時取得滑鼠指定li
裡的類別為dropdown
的元素的大小和相對位置getBoundingClientRect()
,以及nav
元素的大小和相對位置。
將dropdown
的元素的寬、高度加入到類別為open
的div
元素中,讓div
元素跟dropdown
一樣寬、高。
而div
的X軸位置為dropdown
的離螢幕左邊界的距離扣除nav
元素離左邊界的距離,Y軸位置為dropdown
的離螢幕上邊界的距離扣除nav
元素離上邊界的距離,原因為getBoundingClientRect()
是取得元素離畫面的距離,但div
元素在nav
元素中,因此要計算div
在nav
元素中的距離,要扣除nav
和div
重複的部分,才能得到div
在nav
中的距離。
之後計算出來的距離用transform:translate()
來位移div
離nav
元素距離。
這樣就做出效果。
<ul class="cool">
<li>
<a href="#">About Me</a>
<div class="dropdown dropdown1">
<div class="bio">
<img src="https://logo.clearbit.com/wesbos.com">
<p>Wes Bos sure does love web development. He teaches things like JavaScript, CSS and BBQ. Wait. BBQ isn't part of web development. It should be though!</p>
</div>
</div>
</li>
<li>
<a href="#">Courses</a>
<ul class="dropdown courses">
<li>
<span class="code">RFB</span>
<a href="https://ReactForBeginners.com">React For Beginners</a>
</li>
<li>
<span class="code">ES6</span>
<a href="https://ES6.io">ES6 For Everyone</a>
</li>
<li>
<span class="code">STPU</span>
<a href="https://SublimeTextBook.com">Sublime Text Power User</a>
</li>
<li>
<span class="code">WTF</span>
<a href="http://flexbox.io">What The Flexbox?!</a>
</li>
<li>
<span class="code">LRX</span>
<a href="http://LearnRedux.com">Learn Redux</a>
</li>
<li>
<span class="code">CLPU</span>
<a href="http://CommandLinePowerUser.com">Command Line Power User</a>
</li>
<li>
<span class="code">MMD</span>
<a href="http://MasteringMarkdown.com">Mastering Markdown</a>
</li>
</ul>
</li>
<li>
<a href="#">Other Links</a>
<ul class="dropdown dropdown3">
<li><a class="button" href="http://twitter.com/wesbos">Twitter</a></li>
<li><a class="button" href="http://facebook.com/wesbos.developer">Facebook</a></li>
<li><a class="button" href="http://wesbos.com">Blog</a></li>
<li><a class="button" href="http://wesbos.com/courses">Course Catalog</a></li>
</ul>
</li>
</ul>
</nav>
Element.clientHeight
Element.clientHeight 唯讀屬性會回傳元素內部高度(像素),包含 padding 但並未包含滾動條、border、margin。clientHeight 可以被計算成 CSS height + CSS padding - 滾動條的高度(如果有顯示)
Element.clientWidth
Element.clientWidth 唯讀屬性會回傳元素內部寬度(像素),包含 padding 但並未包含滾動條、border、margin。clientWidth 可以被計算成 CSS width + CSS padding - 滾動條的高度(如果有顯示)
Element.clientTop
Element.clientTop為距離上邊界的距離。不包含內距和外距。
Element.clientLeft
Element.clientLeft為距離左邊界的距離。不包含內距和外距。
Element.scrollHeight
Element.scrollHeight為元素真實的高度。包含內距。
Element.scrollWidth
Element.scrollWidth為元素真實的寬度。包含內距。
Element.scrollTop
Element.scrollTop為元素滾動時離元素上邊界的距離
Element.scrollLeft
Element.scrollTop為元素滾動時離元素左邊界的距離
HTMLElement.offsetHeight
HTMLElement.offsetHeight為元素的高度,包含內距,邊界,外距。
HTMLElement.offsetWidth
HTMLElement.offsetHeight為元素的寬度,包含內距,邊界,外距。
HTMLElement.offsetTop
HTMLElement.offsetTop為元素左上角距離HTMLElement.offsetParent元素上邊界的距離。
HTMLElement.offsetLeft
HTMLElement.offsetTop為元素左上角距離HTMLElement.offsetParent元素左邊界的距離。
HTMLElement.offsetParent
HTMLElement.offsetParent為目標元素的最近定位元素。
will-changewill-change
先告訴瀏覽器哪些屬性可以被改變,達到優化的變化。
The will-change CSS property provides a way for authors to hint browsers about the kind of changes to be expected on an element, so that the browser can set up appropriate optimizations ahead of time before the element is actually changed.
/* Keyword values */
will-change: auto;
will-change: scroll-position;
will-change: contents;
will-change: transform; /* Example of <custom-ident> */
will-change: opacity; /* Example of <custom-ident> */
will-change: left, top; /* Example of two <animateable-feature> */
Element